package com.semidata.trp.business.report;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import com.semidata.rtd.core.http.client.HttpClient;
import com.semidata.rtd.core.http.client.HttpClientErrorHandler;
import com.semidata.rtd.core.http.client.HttpResponseHandler;
import com.semidata.rtd.core.log.Logger;
import com.semidata.rtd.core.service.Service;
import com.semidata.rtd.core.service.ServiceManager;
import com.semidata.trp.business.data.ServerData;
import com.semidata.trp.business.model.DecisionServerModel;
import com.semidata.trp.business.utils.ReportUtils;
import com.semidata.trp.message.business.Business;
import com.semidata.trp.message.business.SyncReport;

public class SyncReportService implements Service {

	private boolean quit = false;
	private Thread thread;
	private final int SLEEP_TIME = 1000;

	private HttpClient client;
	private AtomicReference<SyncReport.SyncReportData> syncData;

	@Override
	public void start() {

		syncData = new AtomicReference<>();
		
		client = new HttpClient();
		client.start();

		thread = new Thread(new Runnable() {

			@Override
			public void run() {
				while (!quit) {
					SyncReport.SyncReportData data = getSyncReportData();
					syncData.set(data);
					try {
						sendSyncData();
					} catch (Exception e) {
						Logger.log("ERROR", e);
					}
					long time = System.currentTimeMillis() + 1;
					long sleepTime = SLEEP_TIME - time % SLEEP_TIME;
					if (sleepTime < SLEEP_TIME / 5)
						sleepTime += SLEEP_TIME;
					try {
						Thread.sleep(sleepTime);
					} catch (InterruptedException e) {
						// e.printStackTrace();
					}
				}
			}

		});

		thread.start();

	}

	@Override
	public void stop() {
		quit = true;

		try {
			thread.join();
		} catch (InterruptedException e) {
			// e.printStackTrace();
		}
		client.stop();
	}

	public void sendSyncData() {
		try {
			ServerData serverData = new ServerData();
			List<DecisionServerModel> servers = serverData.getDecisionList();

			for (DecisionServerModel server : servers) {
				String syncUrl = server.getReportUrl();
				if (syncData.get() == null) {
					continue;
				}
				client.send(syncUrl, syncData.get().toByteArray(), null, null,
				        new HttpResponseHandler() {

					        @Override
					        public void handle(HttpResponse response,
					                ByteBuf buffer) {
						        // TODO Auto-generated method stub
						        if (response.getStatus().equals(
						                HttpResponseStatus.OK)) {
							        Logger.log("DEBUG",
							                "send report data to  ok");
							        Logger.log("INFO",
							                "send report data to  ok");

						        } else {
							        Logger.log("DEBUG",
							                "send report data to  error: bad response");
							        Logger.log("INFO",
							                "send report data to  error: bad response");
						        }

					        }
				        }, new HttpClientErrorHandler() {

					        @Override
					        public void handle(ChannelFuture future) {
						        // TODO Auto-generated method stub
						        Logger.log("DEBUG",
						                "report data is received by but error occurs");
						        Logger.log("INFO",
						                "report data is received by but error occurs");
					        }
				        }, null);
			}
		} catch (Exception e) {
			Logger.log("DEBUG", "send sync data to decision fail");
			Logger.log("INFO", e);
		}
	}

	public SyncReport.SyncReportData getSyncReportData() {
		ReportData baseData = ServiceManager.getService(ReportService.class)
		        .getBaseData();
		ReportData currentData = ServiceManager.getService(ReportService.class)
		        .getCurrentData();
		SyncReport.SyncReportData.Builder reportData = SyncReport.SyncReportData
		        .newBuilder();
		Set<MonitorReportData> currentReportData = currentData
		        .getMonitorReportData();
		Set<MonitorReportData> baseReportData = baseData.getMonitorReportData();
		Set<Integer> orgCostIds = new HashSet<>();
		Set<Integer> strategyCostIds = new HashSet<>();
		Set<Integer> strategyImprIds = new HashSet<>();
		Set<Integer> strategyClickIds = new HashSet<>();
		Set<Integer> resourceImprIds = new HashSet<>();
		Set<Integer> resourceClickIds = new HashSet<>();
		for (MonitorReportData data : currentReportData) {
			orgCostIds.addAll(data.getOrganizationCosts().keySet());
			strategyCostIds.addAll(data.getStrategyCosts().keySet());
			strategyImprIds.addAll(data.getStrategyImpressions().keySet());
			strategyClickIds.addAll(data.getStrategyClicks().keySet());
			resourceImprIds.addAll(data.getResourceImpressions().keySet());
			resourceClickIds.addAll(data.getResourceClicks().keySet());
		}
		for (Integer id : orgCostIds) {
			SyncReport.Cost orgCost = ReportUtils.getOrganizationCost(
			        baseReportData, currentReportData, id);
			reportData.addOrgCostList(orgCost);
		}
		for (Integer id : strategyCostIds) {
			SyncReport.Cost strategyCost = ReportUtils.getStrategyCost(
			        baseReportData, currentReportData, id);
			reportData.addStrategyCostList(strategyCost);
		}
		for (Integer id : strategyImprIds) {
			SyncReport.StrategyImpr strategyImpr = ReportUtils
			        .getStrategyImpression(baseReportData, currentReportData,
			                id);
			reportData.addStrategyImprList(strategyImpr);
		}
		for (Integer id : strategyClickIds) {
			SyncReport.Click strategyClick = ReportUtils.getStrategyClick(
			        baseReportData, currentReportData, id);
			reportData.addStrategyClickList(strategyClick);
		}
		for (Integer id : resourceImprIds) {
			SyncReport.Impression resourceImpression = ReportUtils
			        .getResourceImpression(baseReportData, currentReportData,
			                id);
			reportData.addResourceImprList(resourceImpression);
		}
		for (Integer id : resourceClickIds) {
			SyncReport.Click resourceClick = ReportUtils.getResourceClick(
			        baseReportData, currentReportData, id);
			reportData.addResourceClickList(resourceClick);
		}
		return reportData.build();

	}

}
